/**
 * WAF JavaScript Application Framework v1.0
 * Premium Technology, Inc. ml
 * 
 * @since 2012-01-05, v1.0
 * @update 2014-04-03, v1.2
 * 
 */

var JSAF = new Object();
JSAF.version = 1.2;

/************************************************************************************* 
 * Basic Javascript language utilities
 *************************************************************************************/
/** trim leading/ending white spaces of a string
 * @param sText the string
 * @return string with leading/ending white spaces trimmed
 */
JSAF.stringTrim = function(sText) {
  return(JSAF.stringRTrim(JSAF.stringLTrim(sText)));
}
/** trim leading white spaces of a string
 * @param sText the string
 * @return string with leading white spaces trimmed
 */
JSAF.stringLTrim = function(sText) {
  return(sText.replace(/^\s\s*/, ''));
}
/** trim ending white spaces of a string
 * @param sText the string
 * @return string with ending white spaces trimmed
 */
JSAF.stringRTrim = function(sText) {
  var re = /\s/, i = sText.length;
  while (re.test(sText.charAt(--i)));
  return(sText.substring(0, i + 1));
}
/** check whether an object is Array or not
 * @param obj the object
 * @return true or false
 */
JSAF.isArray = function(obj) {
  return(obj.constructor == Array);  // return(Object.prototype.toString.call(obj) === "[object Array]");
}
/** change hashMap to array
 * @param map the HashMap which defined in collections.js
 * @return array
 */
JSAF.hashMapToArray = function(map) {
	if (map.keySet) {
		var arrays = new Array();
		var keySet = map.keySet();
		for (var i = 0; i < map.size(); i++) {
			var key = keySet[i];
			var value = map.get(key);
			var pObj = new Object();
			pObj.name = key;
			pObj.value = value;
			arrays[key] = pObj;
		}
		return arrays;
	} else {
		throw new Error("the param map is not hashMap, please check.");
	}
}
/**
 * string array to csv string.
 */
JSAF.StringArrayToCsvString = function(array) {
	if (array == null || array.length == 0) return "";
	var csvString = "";
	for (var i = 0; i < array.length; i++) {
		if (i == 0) {
			csvString = csvString + array[i];
		} else {
			csvString = csvString + "," + array[i];
		}
	}
	return csvString;
}
/**
 * check whether an object can not find.
 * @param obj the object
 * @return true or false
 */
JSAF.isObjectUndefined = function(obj) {
  return obj == null || typeof(obj) == "undefined";
}
/** escape a character in the string
 * @param sText the string text
 * @param sChar the character to be escaped, i.e. ' or "
 * @return the new string text
 */
JSAF.stringEscapeChar = function(sText, sChar) {
  if (sText.indexOf(sChar) < 0) return(sText);
  return(sText.replace((new RegExp(sChar, "g")), "\\" + sChar));
}
/** get number of properties that an object contains (doesn't count the properties contained in its prototype chains)
 * @param obj the object
 * @return number of properties
 */
JSAF.keysCount = function(obj) {
  if (obj == null) return(0);
  var c = 0;
  for (var n in obj) if (obj.hasOwnProperty(n)) ++c;
  return(c);
}
/** wrap an object's function to a function variable to be dynamically called later
 * @param obj the object
 * @param strFuncName string function name in the object
 * @return function variable
 */
JSAF.wrapObjectFunctionToVariable = function(obj, strFuncName) {
  return (function() { return(obj[strFuncName].apply(obj, arguments)); });
}
/***************************
 JSON (JavaScript Object Notation) utility. Some browsers already have native global object JSON
 in case the JSON is not provided by the browser, then here define it with two basic functions parse() and stringify()
 **************************/
if (typeof(JSON) == "undefined") {  JSON = new Object(); }
if (JSON.parse == null) {
  /** parse string JSON into Object
   * @param sJson the string of JSON
   * @return JSON object
   */
  JSON.parse = function(sJson) {
    if (sJson == null || sJson == "") sJson = "''";
    return(eval("(" + sJson + ")"));
  };
}
if (JSON.stringify == null) {
  /** serialize JSON object to string
   * @param oJson the JSON object
   * @return string
   */
  JSON.stringify = function(oJson) {
    var t = typeof(oJson);
    if (t != "object" || oJson === null) {
      if (t == "string") return(oJson);
      return(String(oJson));
    } else {
      var n, v, json = [], isAry = (oJson && JSAF.isArray(oJson));
      for (n in oJson) {
        v = oJson[n]; t = typeof(v);
        if (t == "string") v = '"' + JSAF.stringEscapeChar(v, '"') + '"';  // char " in v should be escaped using \"
        else if (t == "object" && v !== null) v = JSON.stringify(v);  // recurse array or object
        json.push((isAry ? "" : '"' + n + '":') + String(v));
      }
      return((isAry ? "[" : "{") + String(json) + (isAry ? "]" : "}"));
    }
  };
}
/**
 * stop event propagation
 */
JSAF.stopPropagation = function(oEvent) {
  if (JSAF.isBrowserIE()) {
	oEvent.cancelBubble = true;
  } else {
  	oEvent.stopPropagation();
  }
}
/**
 * get waf popup
 */
JSAF.getWafPopup = function() {
   var popupMgr = null;
   if (parent && typeof(parent.WafPopup) != "undefined") popupMgr = parent.WafPopup;
   else if (typeof(WafPopup) != "undefined") popupMgr = WafPopup;
   else if (top && typeof(top.WafPopup) != "undefined") popupMgr = top.WafPopup;
   return popupMgr;
}
/**
 * multiBrowser compatibility of window.createPopup()
 * window.createPopup() is  only support IE browser ,
 * this method can let the other browser to support the "window.createPopup()".
 */
JSAF.createWafPopup = function() {
	if(!window.createPopup) {
		var _createPopup = function() {
			var SetElementStyles = function(element, styleDict) {
				var style = element.style;
				for (var styleName in styleDict) {
					style[styleName] = styleDict[styleName]; 
				}
			}
			var eDiv = document.createElement("div"); 
			SetElementStyles(eDiv, {'position': 'absolute', 'top': 0 + 'px', 'left': 0 + 'px', 'width': 0 + 'px', 'height': 0 + 'px', 'zIndex': 1000, 'display': 'none', 'overflow': 'hidden'});
			eDiv.body = eDiv;
			var opened = false;
			var setOpened = function(b) {
				opened = b; 
			}
			var getOpened = function() {
				return opened; 
			}
			var getCoordinates = function(oElement) {
				var coordinates = {x: 0, y: 0}; 
				while(oElement) {
					coordinates.x += oElement.offsetLeft;
					coordinates.y += oElement.offsetTop;
					oElement = oElement.offsetParent;
				}
				return coordinates;
			}
			return {
				htmlTxt: "",
			    document: eDiv,
			    isOpen : getOpened(),
			    isShow: false,
			    hide: function() { 
			    	SetElementStyles(eDiv, {'top': 0 + 'px', 'left': 0 + 'px', 'width': 0 + 'px', 'height': 0 + 'px', 'display': 'none'});
			    	eDiv.innerHTML = "";
			    	this.isShow = false;
		    	},
			    show: function(iX, iY, iWidth, iHeight, oElement) {
		    		if(!getOpened()) {
		    			document.body.appendChild(eDiv);
		    			setOpened(true);
		    		}
		    		this.htmlTxt = eDiv.innerHTML;
		    		if (this.isShow) { 
		    			this.hide(); 
		    		}
		    		eDiv.innerHTML = this.htmlTxt;
		    		var coordinates = getCoordinates(oElement);
		    		eDiv.style.top = (iX + coordinates.x) + 'px';
		    		eDiv.style.left = (iY + coordinates.y) + 'px';
		    		eDiv.style.width = iWidth + 'px';
		    		eDiv.style.height = iHeight + 'px';
		    		eDiv.style.display = 'block';
		    		this.isShow = true; 
			    }
		    }
		}
		window.createPopup = function() {
			return _createPopup(); 
		}
	}
	return window.createPopup();
}
/**
 * get the scrollbar width
 */
JSAF.getScrollbarWidth = function() {
	var scrollDiv = document.createElement("div");
	scrollDiv.className = "scrollbar-measure";
	document.body.appendChild(scrollDiv);	
	var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;
	document.body.removeChild(scrollDiv);
	return scrollbarWidth;
}
/** parse JSON string into JSON object. 
 * JSON sample: { fName : "Tom", address : { "addr.line1" : "32 Broadway", "addr:city" : "New York", "" : "" }, "phone.work" : "2128555511" }. The property name could be quoted.
 * To access the property, obj.fName or obj["fName"],  obj.address["addr.line1"], obj.address["addr:city"]
 * @param sJson the string of JSON
 * @return JSON object
 */
JSAF.jsonStringToObject = function(sJson) {
  return(JSON.parse(sJson));
}
/** serialize JSON object into JSON string
 * @param oJson the JSON object
 * @return the string of JSON
 */
JSAF.jsonObjectToString = function(oJson) {
  return(JSON.stringify(oJson));
}
/** parse xml string into Xml DOM object
 * @param sXml the xml string 
 * @return Xml Dom Object
 */
JSAF.xmlStringToDom = function(sXml) {
  var xmlDoc = null;
  if (window.DOMParser) {  // Non-IE
    xmlDoc = (new window.DOMParser()).parseFromString(sXml, "text/xml");
  } else {  // IE
    xmlDoc = new ActiveXObject("MSXML.DOMDocument"); // or "Microsoft.XMLDOM"
    xmlDoc.async = false;
    xmlDoc.loadXML(sXml);
  }
  return(xmlDoc);
}
/** serialize Xml Dom Object to xml string
 * @param oXmlDoc the Xml Dom Object
 * @return Xml string
 */
JSAF.xmlDomToString = function(oXmlDoc) {
  if (window.XMLSerializer) {  // Non-IE
    return((new window.XMLSerializer()).serializeToString(oXmlDoc));
  } else {  // IE
    return(oXmlDoc.documentElement.xml);
  }
}
/** convert XML DOM object to JSON object. A sample:
 * xml :  <root id="100" code="C1"><group>Grp01</group><employee code="S01">test msg<fName>Tom</fName><lName>Smith</lName></employee><employee code="S02">msg2<fName>John</fName><lName>Ko</lName></employee><root> 
 * JSON:  { "root":{ "xmlAttributes":{"id":"100", "code":"C1"}, "group":"Grp01", "employee":[{ "xmlAttributes":{"code":"S01"}, "xmlText":"test msg", "fName":"Tom", "lName":"Smith" }, { "xmlAttributes":{"code":"S02"}, "xmlText":"msg", "fName":"John", "lName":"Ko" }] }}
 * @param oXmlDoc the XML DOM object
 * @return XML JSON object
 */
JSAF.xmlDomToJson = function(oXmlDoc) {
  var oJson = {};
  if (oXmlDoc.attributes != null && oXmlDoc.attributes.length > 0) {
    var att = {}, n, v;
    for (var i = 0; i < oXmlDoc.attributes.length; ++i) att[oXmlDoc.attributes[i].name] = oXmlDoc.attributes[i].value;
    oJson["xmlAttributes"] = att;
  }
  if (oXmlDoc.childNodes != null && oXmlDoc.childNodes.length > 0) {
    if (oJson["xmlAttributes"] == null && oXmlDoc.childNodes.length == 1 && oXmlDoc.childNodes[0].nodeType == 3) {
      oJson = oXmlDoc.childNodes[0].nodeValue;
    } else {
      var nod, n, v, cv;
      for (var i = 0; i < oXmlDoc.childNodes.length; ++i) {
        nod = oXmlDoc.childNodes[i];
        if (nod.nodeType == 3) { // TextNode
          oJson["xmlText"] = nod.nodeValue;
        } else if (nod.nodeType == 1) {  // element
          n = nod.nodeName; 
          v = JSAF.xmlDomToJson(nod);  // recursive
          cv = oJson[n];
          if (cv == null) {
            oJson[n] = v;
          } else if (JSAF.isArray(cv)) {
            cv.push(v);
          } else {
            oJson[n] = [ cv, v ];
          }
        }
      }
    }
  }
  return(oJson);
}
/** convert XML JSON object to XML DOM object
 * @param oXmlJson the XML JSON object
 * @param oDomNode (optional) the Dom Node object under which the dom object to be added. If it is null or not provided then a new Document will be created.
 * @return XML DOM Document object if oDomNode is not provided, or return oDomNode if oDomNode is provided.
 */
JSAF.xmlJsonToDom = function(oXmlJson, oDomNode) {
  var domDoc, n, v;
  if (oDomNode == null) {
    var np = JSAF.keysCount(oXmlJson);
    if (np == 0) return(null);
    if (np == 1) {
      for (n in oXmlJson) v = oXmlJson[n];
      domDoc = JSAF.xmlStringToDom("<" + n + "/>");
      oXmlJson = v;
    } else { domDoc = JSAF.xmlStringToDom("<root/>"); }
    JSAF.xmlJsonToDom(oXmlJson, domDoc.documentElement);
    return(domDoc);
  }
  domDoc = oDomNode.ownerDocument;
  var t = typeof(oXmlJson), n1;
  if (t == "string" || t == "number" || t == "boolean") {
    oDomNode.appendChild(domDoc.createTextNode(String(oXmlJson)));
  } else if (JSAF.isArray(oXmlJson)) {
    for (var i = 0; i < oXmlJson.length; ++i) {
      if (i == 0) JSAF.xmlJsonToDom(oXmlJson[i], oDomNode);
      else {
        var e = domDoc.createElement(oDomNode.nodeName);
        oDomNode.parentNode.appendChild(e);
        JSAF.xmlJsonToDom(oXmlJson[i], e);
      }
    }
  } else if (t == "object") {
    for (n in oXmlJson) {
      v = oXmlJson[n];
      if (n == "xmlAttributes") {
        for (n1 in v) oDomNode.setAttribute(n1, v[n1]);
      } else if (n == "xmlText") {
        oDomNode.appendChild(domDoc.createTextNode(String(v)));
      } else {
        var e = domDoc.createElement(n);
        oDomNode.appendChild(e);
        JSAF.xmlJsonToDom(v, e);
      }
    }
  } else {
    oDomNode.appendChild(domDoc.createTextNode(String(oXmlJson)));
  }
  return(oDomNode);
}
/** parse XML string into JSON object
 * @param sXml the XML string
 * @return JSON object
 */
JSAF.xmlStringToJson = function(sXml) {
  return(JSAF.xmlDomToJson(JSAF.xmlStringToDom(sXml)));
}
/** serialize JSON object into XML string
 * @param oXmlJson the XML JSON object
 * @return XML string
 */
JSAF.xmlJsonToString = function(oXmlJson) {
  return(JSAF.xmlDomToString(JSAF.xmlJsonToDom(oXmlJson)));   // or directly output to xml string
}
/** parse java properties (name=value\n) string into JSON object. Each name=value is in one line (line separator is char \n). if a line ends with char \ then it continues to next line.
 * @param sProps the string of java properties in (name=value\n)
 * @return JSON object so that the properties can be accessed by obj[propName] or obj.propName
 */
JSAF.javapropsStringToJson = function(sProps) {
  if (sProps == null || sProps == "") return(null);
  var ary = sProps.split("\n");
  var json = new Object();
  var tmp, n, v;
  var i = 0;
  while (i < ary.length) {
    tmp = JSAF.stringTrim(ary[i]);
    if (tmp.length > 1 && tmp.charAt(0) != "#" && tmp.charAt(0) != "!") {
      while (tmp.charAt(tmp.length - 1) == "\\") {
        tmp = tmp.substring(0, tmp.length - 1);
        if (i < ary.length - 1) {
          tmp += JSAF.stringTrim(ary[++i]);
        } else break;
      }
      var k = tmp.indexOf("=");
      if (k > 0) {
        n = JSAF.stringRTrim(tmp.substring(0, k));
        v = JSAF.stringLTrim(tmp.substring(k + 1));
        json[n] = v.replace(/\\n/g, "\n").replace(/\\t/g, "\t");
      }
    }
    ++i;
  }
  return(json);
}
/** serialize JSON object into java properties string (name=value\n)
 * @param oPropsJson the JSON object
 * @return string of java properties. It only output the first level properties
 */
JSAF.javapropsJsonToString = function(oPropsJson) {
  if (oPropsJson == null || typeof(oPropsJson) != "object") return("");
  var n, v, t, sProps = "";
  for (n in oPropsJson) {
    v = oPropsJson[n]; t = typeof(v);
    if (t != "string") v = String(v);
    if (sProps.length > 0) sProps += "\n";
    if (v.indexOf("\n") >= 0) v = v.replace(/\n/g, "\\\n");
    sProps += n + "=" + v;
  }
  return(sProps);
}
/** JavaScript Logger, LOG4JS
 * The messages are logged in a separate popup window with url jsp/common/appJsConsole.jsp. 
 * if a page needs to use JS logger, please call JSAF.jsLogger.setLevel(level) in the beginning of page. level could be JSAF.JSLOG_LEVEL_DEBUG, JSAF.JSLOG_LEVEL_INFO, JSAF.JSLOG_LEVEL_WARN, JSAF.JSLOG_LEVEL_ERROR, JSAF.JSLOG_LEVEL_FATAL, JSAF.JSLOG_LEVEL_NONE
 *   JSAF.jsLogger.setLevel(JSAF.JSLOG_LEVEL_DEBUG);
 * use following methods to log messages:
 *  if (JSAF.jsLogger.isDebugEnabled()) JSAF.jsLogger.debug(msg, err);
 *  if (JSAF.jsLogger.isInfoEnabled())  JSAF.jsLogger.info(msg, err);
 *  if (JSAF.jsLogger.isWarnEnabled())  JSAF.jsLogger.warn(msg, err);
 *  if (JSAF.jsLogger.isErrorEnabled()) JSAF.jsLogger.error(msg, err);
 * 
 * @since 2010-11-06, update 2012-01-10
 */
JSAF.JSLOG_LEVEL_FATAL = 5;
JSAF.JSLOG_LEVEL_ERROR = 4;
JSAF.JSLOG_LEVEL_WARN  = 3;
JSAF.JSLOG_LEVEL_INFO  = 2;
JSAF.JSLOG_LEVEL_DEBUG = 1;
JSAF.JSLOG_LEVEL_NONE  = 0;
JSAF.jsLogger = {
  win : null,  // the logger window object
  level : JSAF.JSLOG_LEVEL_NONE,
  cnt : 0,
  
  // set the logging level. iLevel could be one of JSAF.JSLOG_LEVEL_XXXX
  setLevel : function(iLevel) {  this.level = (typeof(iLevel) != "number") ? 0 : iLevel; },
  // disable the JS logger
  disable : function() {
    this.level = JSAF.JSLOG_LEVEL_NONE;
    try {
      if (this.win != null && this.win.closed == false) this.win.close();
      this.win = null;
    } catch(e) {}
  },
  isDebugEnabled : function() {  return(this.level > JSAF.JSLOG_LEVEL_NONE && this.level <= JSAF.JSLOG_LEVEL_DEBUG); },
  isInfoEnabled  : function() {  return(this.level > JSAF.JSLOG_LEVEL_NONE && this.level <= JSAF.JSLOG_LEVEL_INFO);  },
  isWarnEnabled  : function() {  return(this.level > JSAF.JSLOG_LEVEL_NONE && this.level <= JSAF.JSLOG_LEVEL_WARN);  },
  isErrorEnabled : function() {  return(this.level > JSAF.JSLOG_LEVEL_NONE && this.level <= JSAF.JSLOG_LEVEL_ERROR); },
  isFatalEnabled : function() {  return(this.level > JSAF.JSLOG_LEVEL_NONE && this.level <= JSAF.JSLOG_LEVEL_FATAL); },
  isEnabled      : function() {  return(this.level > JSAF.JSLOG_LEVEL_NONE && this.level <= JSAF.JSLOG_LEVEL_FATAL); },
  /** log debug/info/warn/error/fatal message into application JS console window
   * @param sMsg (string) the message to be logged
   * @param oErr (Error object) the optional Error object to be logged
   */
  debug : function(sMsg, oErr) {  this.log(sMsg, oErr, JSAF.JSLOG_LEVEL_DEBUG); },
  info  : function(sMsg, oErr) {  this.log(sMsg, oErr, JSAF.JSLOG_LEVEL_INFO);  },
  warn  : function(sMsg, oErr) {  this.log(sMsg, oErr, JSAF.JSLOG_LEVEL_WARN);  },
  error : function(sMsg, oErr) {  this.log(sMsg, oErr, JSAF.JSLOG_LEVEL_ERROR); },
  fatal : function(sMsg, oErr) {  this.log(sMsg, oErr, JSAF.JSLOG_LEVEL_FATAL); },
  log   : function(sMsg, oErr, iLevel) {
    if (this.level > JSAF.JSLOG_LEVEL_NONE && this.level <= iLevel) { window.setTimeout(function() { var m=sMsg; var e=oErr; var l=iLevel; JSAF.jsLogger._logThread_(m, e, l); }, 0); }
  },
  _logThread_ : function(sMsg, oErr, iLevel, bWait) {
    if (this.cnt > 0 && (this.win == null || this.win.closed) && (bWait == null || bWait == true)) {
      window.setTimeout(function() { var m=sMsg; var e=oErr; var l=iLevel; JSAF.jsLogger._logThread_(m, e, l, false); }, 300);
      return;
    }
    this.cnt++;
    if (this.win == null || this.win.closed) {
      this.win = window.open("jsp/common/appJsConsole.jsp", "AppJsConsole", "width=700,height=520,location=no,toolbar=no,menubar=no,titlebar=yes,resizable=yes,directories=no,status=yes,scrollbars=yes");
    }
    var sL = "";
    if (iLevel == JSAF.JSLOG_LEVEL_DEBUG) sL = "DEBUG";
    else if (iLevel == JSAF.JSLOG_LEVEL_INFO) sL = "INFO";
    else if (iLevel == JSAF.JSLOG_LEVEL_WARN) sL = "WARN";
    else if (iLevel == JSAF.JSLOG_LEVEL_ERROR) sL = "ERROR";
    else if (iLevel == JSAF.JSLOG_LEVEL_FATAL) sL = "FATAL";
    else sL = "DEBUG";
    var ts = new Date();
    var sTS = "" + ts.getFullYear() + "-" + (ts.getMonth() + 1) + "-" + ts.getDate() +
          "T" + ts.getHours() + ":" + ts.getMinutes() + ":" + ts.getSeconds() + "." + ts.getMilliseconds();
    var url = window.location;
    try { this.win.log(sL, sTS, url, sMsg, oErr); } catch(e) {}
    this.cnt--;
  }
}
/** eval a js script code (parse the js code and execute it) within the global context.
 * the standard js function eval() execute the code within current context.
 * @param sScript (string) the js script code (without <script> tag in the beginning and end)
 */
JSAF.evalGlobal = function(sScript) {
  if (sScript == null || sScript == "") return;
  if (window.execScript) window.execScript(sScript);  // IE
  else eval.call(null, sScript);
}
/** load a js file from web server and execute the script
 * @param sJsFile the js file name. 
 *   If it starts with "http:" or "https:" then it is a full absolute URL such as "http://host/waf/js/wafjsaf.js"; 
 *   else if it starts with "/" then it is a URL without "protocol://host:port" which will be from window.location.href
 *   otherwise it is a relative URL to the current page (window.location.href)
 * @param bAsync (optional) true (load file in async mode), false (load file in synch mode). default is true
 */
JSAF.loadJsFile = function(sJsFile, bAsync) {
  if (sJsFile == null || sJsFile == "") return;
  if (JSAF.jsLogger.isDebugEnabled()) JSAF.jsLogger.debug("loading js file: " + sJsFile);
  var ajaxInfo = {
    url : sJsFile,
    handler : function(sScript) {
      if (JSAF.jsLogger.isDebugEnabled()) JSAF.jsLogger.debug("executing js file: " + this.url);
      if (this.getHttpResponseStatusCode() == 200) JSAF.evalGlobal(sScript);
      else if (JSAF.jsLogger.isErrorEnabled()) JSAF.jsLogger.error("error loading js file: " + this.url);
    },
    async : (bAsync == null || bAsync == true) ? true : false,
    returnDataType : JSAF.AJAX_DATA_TYPE_TEXT
  }
  JSAF.getAjaxWorker(ajaxInfo).send();
}

/************************************************************************************* 
 * DHTML utilities
 *************************************************************************************/
/** get current browser name
 * @return browser name string
 */
JSAF.getBrowserName = function() {
  return(window.navigator.appName);
}
/**
 * @return true if current browser is IE
 */
JSAF.isBrowserIE = function() {
  return((window.navigator.appName == 'Microsoft Internet Explorer')||(navigator.userAgent.indexOf("Trident") >= 0));// for IE 11;
}
/**
 * @return true if current browser is Firefox
 */
JSAF.isBrowserFirefox = function() {
  return(navigator.userAgent.indexOf("Firefox") >= 0);
}
/**
 * @return true if current browser is Chrome google
 */
JSAF.isBrowserGoogle = function() {
  return(navigator.userAgent.indexOf("Chrome") >= 0);
}
/**
 * @return true if current browser is Safari
 */
JSAF.isBrowserSafari = function() {
  return(navigator.userAgent.indexOf("Safari") >= 0);
}
/**
 * @return true if current browser is Opera
 */
JSAF.isBrowserOpera = function() {
  return(navigator.userAgent.indexOf("Opera") >= 0);
}
/**
 * is Browser IE in the compatibility view mode or not
 * if IE version number is 6.0, 7.0, 8.0 or 9.0 in any view mode or IE version number is 10.0 or 11.0 in compatibility view mode return true. 
 * if IE version number is 10.0 or 11.0 not in compatibility view mode return false.
 * @return 
 */
JSAF.isBrowserIEInCompMode = function() {
  return (JSAF.getIEVersion() > 0 && JSAF.getIEVersion() < 10);
}
/** get IE version.
 * @return IE version number 6.0, 7.0, etc. -1 means non-IE browser
 */
JSAF.getIEVersion = function() {
  var rv = -1;
  if (JSAF.isBrowserIE()) {
    var ua = window.navigator.userAgent;
    var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})");
    if (re.exec(ua) != null) {
    	rv = parseFloat( RegExp.$1 );
    } else {
    	//for IE11
    	re = new RegExp("rv:([0-9]{1,}[\.0-9]{0,})");
		if(re.exec(ua) != null) rv = parseFloat( RegExp.$1 );
    }
  }
  return rv;
}
/** get the browser which is support the xml format display.
 * @return false (when the browser is "Chrome" or "Safari")
 * @return true (the other browser)
 */
JSAF.isBrowserSupportXML = function() {
	if(JSAF.isBrowserGoogle() || JSAF.isBrowserSafari() || JSAF.getIEVersion() > 9) {
		return false;
	} else 
		return true;
}
/** add a cookie
 * @param sName the cookie name
 * @param sValue the cookie value
 * @param sDomain the cookie domain. default is current server host name. "1" for last part (com), "2" for last 2 parts (premiumit.com)
 * @param sPath the cookie path. default is "/"
 * @param bSecure true/false whether use secure cookie
 * @param iMaxAgeMinutes integer the maximum cookie age in minutes. default is -1 (memory cookie, it will be deleted when browser closes) 
 */
JSAF.addCookie = function(sName, sValue, sDomain, sPath, bSecure, iMaxAgeMinutes) {
  var ck = sName + "=" + escape(sValue) + "; path=" + (sPath == null ? "/" : sPath) + "; ";
  if (sDomain != null) {
    if (sDomain.length == 1 && !isNaN(sDomain)) {
      var hn = window.location.hostname;
      var n = parseInt(sDomain), i, j = 0, i0 = hn.length;
      while (j < n) {
        i = hn.lastIndexOf(".", i0 - 1);
        if (i < 0) break;
        else i0 = i;
        ++j;
      }
      if (i0 == hn.length || j < n) sDomain = hn;
      else sDomain = hn.substring(i0);
    }
    ck += "domain=" + sDomain + "; ";
  }
  if (bSecure != null && bSecure == true) ck += "secure; ";
  if (iMaxAgeMinutes != null && iMaxAgeMinutes > 0) {
    ck += "expires=" + (new Date((new Date()).getTime() + (iMaxAgeMinutes * 60000))).toGMTString() + "; ";
  }
  document.cookie = ck;
}
/** get value of a cookie
 * @param sName the cookie name
 * @return string of the cookie value or null if cookie doesnt exist
 */
JSAF.getCookieValue = function(sName) {
  var cks = document.cookie.split("; ");
  sName = sName + "=";
  var l = sName.length;
  if (cks != null && cks.length > 0) {
    for (var i = 0; i < cks.length; ++i) if (cks[i].substring(0,l) == sName) return(cks[i].substring(l));
  }
  return(null);
}
/** delete a cookie
 * @param sName the cookie name
 */
JSAF.deleteCookie = function(sName) {
  document.cookie = sName + "=; expires=Fri, 21 Dec 1976 04:31:24 GMT;";
}
/** get context path of URL
 * @param sUrl (optional) the url string. if it is null (not provided) then it is the curent page url
 * @return string. i.e. it returns "/waf" for url "http://host:port/waf/signon.do?q1=123&q2=abc". returns "/" for url "http://host:port/signon.do"
 */
JSAF.urlContextPath = function(sUrl) {
  if (sUrl == null) sUrl = window.location.href;
  var i = sUrl.indexOf("//");
  if (i < 0) i = 0;
  else i += 2;
  i = sUrl.indexOf("/", i);
  if (i < 0) return("/");
  var i2 = sUrl.indexOf("/", i + 1);
  if (i2 < 0) {
    i2 = sUrl.indexOf("?", i + 1);
    return(i2 < 0 ? sUrl.substring(i) : sUrl.substring(i, i2));
  } else {
    return(sUrl.substring(i, i2));
  }
}
/** get path info of URL.  URL = urlPathInfo + "/" + urlFileName + "?" + urlQueryString
 * @param sUrl (optional) the url string. if it is null (not provided) then it is the curent page url
 * @return string. i.e. it returns "http://host:port/waf" for url "http://host:port/waf/signon.do?q1=123&q2=abc". returns "http://host:port" for url "http://host:port/abc". returns "http://host:port/abc" for url "http://host:port/abc/"
 */
JSAF.urlPathInfo = function(sUrl) {
  var sUri = JSAF.urlUriInfo(sUrl);
  var i = sUri.lastIndexOf("/");
  if (i < 0) return(null);
  else return(sUri.substring(0, i));
}
/** get file name of URL.
 * @param sUrl (optional) the url string. if it is null (not provided) then it is the curent page url
 * @return string. i.e. it returns "signon.do" for url "http://host:port/waf/signon.do?q1=123&q2=abc". returns "abc" for url "http://host:port/abc". returns null for url "http://host:port/abc/"
 */
JSAF.urlFileName = function(sUrl) {
  var sUri = JSAF.urlUriInfo(sUrl);
  var i = sUri.lastIndexOf("/");
  if (i < 0) return(sUri);
  else if (i == (sUri.length - 1)) return(null);
  else return(sUri.substring(i + 1));
}
/** get file path of URL
 * @param sUrl (optional) the url string. if it is null (not provided) then it is the curent page url
 * @return string. i.e. it returns "/waf/signon.do" for url "http://host:port/waf/signon.do?q1=123&q2=abc". returns "/signon.do" for url "http://host/signon.do"
 */
JSAF.urlFilePath = function(sUrl) {
  if (sUrl == null) sUrl = window.location.href;
  var i = sUrl.indexOf("//");
  if (i < 0) i = 0;
  else i += 2;
  var i2 = sUrl.indexOf("/", i);
  if (i2 < 0) return(null);
  var i3 = sUrl.indexOf("?", i2 + 1);
  if (i3 < 0) return(sUrl.substring(i2));
  else return(sUrl.substring(i2, i3));
}
/** get extra path info (after the context) of URL.
 * @param sUrl (optional) the url string. if it is null (not provided) then it is the curent page url
 * @return string. i.e. it returns "q1=123&q2=abc" for url "http://host:port/waf/signon.do?q1=123&q2=abc". returns null if there are no query string
 */
JSAF.urlQueryString = function(sUrl) {
  if (sUrl == null) sUrl = window.location.href;
  var i = sUrl.indexOf("?");
  if (i >= 0) return( (i == (sUrl.length - 1)) ? null : sUrl.substring(i + 1));
  else return(null);
}
/** get URI info (from protocol up to before the query string) of URL.
 * @param sUrl (optional) the url string. if it is null (not provided) then it is the curent page url
 * @return string. i.e. it returns "http://host:port/waf/signon.do" for url "http://host:port/waf/signon.do?q1=123&q2=abc"
 */
JSAF.urlUriInfo = function(sUrl) {
  if (sUrl == null) sUrl = window.location.href;
  var i = sUrl.indexOf("?");
  if (i >= 0) return(sUrl.substring(0, i));
  else return(sUrl);
}
/** get "protocol" of URL
 * @param sUrl (optional) the url string. if it is null (not provided) then it is the curent page url
 * @return string. i.e. it returns "http" for url "http://host:port/waf/signon.do?q1=123&q2=abc". returns "https" for url "https://host/signon.do"
 */
JSAF.urlProtocol = function(sUrl) {
  if (sUrl == null) sUrl = window.location.href;
  var i = sUrl.indexOf("://");
  if (i < 0) return(null);
  else return(sUrl.substring(0, i));
}
/** get "prot://host:port" of URL
 * @param sUrl (optional) the url string. if it is null (not provided) then it is the curent page url
 * @return string. i.e. it returns "http://host:port" for url "http://host:port/waf/signon.do?q1=123&q2=abc". returns "http://host" for url "http://host/signon.do"
 */
JSAF.urlProtocolHostPort = function(sUrl) {
  if (sUrl == null) sUrl = window.location.href;
  var i = sUrl.indexOf("//");
  if (i < 0) return(null);
  i = sUrl.indexOf("/", (i + 2));
  if (i < 0) return(sUrl);
  else return(sUrl.substring(0, i));
}
/** get "host:port" of URL
 * @param sUrl (optional) the url string. if it is null (not provided) then it is the curent page url
 * @return string. i.e. it returns "host:port" for url "http://host:port/waf/signon.do?q1=123&q2=abc". returns "host" for url "http://host/signon.do"
 */
JSAF.urlHostPort = function(sUrl) {
  if (sUrl == null) sUrl = window.location.href;
  var i = sUrl.indexOf("//");
  if (i < 0) return(null);
  i += 2;
  var i2 = sUrl.indexOf("/", i);
  if (i2 < 0) return(sUrl.substring(i));
  else return(sUrl.substring(i, i2));
}
/** get "host" of URL
 * @param sUrl (optional) the url string. if it is null (not provided) then it is the curent page url
 * @return string. i.e. it returns "host" for url "http://host:port/waf/signon.do?q1=123&q2=abc". returns "host" for url "http://host/signon.do"
 */
JSAF.urlHost = function(sUrl) {
  var hp = JSAF.urlHostPort(sUrl);
  if (hp == null) return(null);
  var i = hp.indexOf(":");
  if (i < 0) return(hp);
  else return(hp.substring(0, i));
}
/** get "port" of URL
 * @param sUrl (optional) the url string. if it is null (not provided) then it is the curent page url
 * @return string. i.e. it returns "port" for url "http://host:port/waf/signon.do?q1=123&q2=abc". returns null for url "http://host/signon.do"
 */
JSAF.urlPort = function(sUrl) {
  var hp = JSAF.urlHostPort(sUrl);
  if (hp == null) return(null);
  var i = hp.indexOf(":");
  if (i < 0) return(null);
  else return(hp.substring(i + 1));
}

/** get the first element with the ID attribute of sElemId.
 * @param sElemId the element id attribute string
 * @param oCtxElem (optional) the context element. if it is null (or it is not provided) then it is document.
 * @return object or null if not found
 */
JSAF.getElementById = function(sElemId, oCtxElem) {
  if (oCtxElem == null) return( JSAF.isObjectUndefined(document.getElementById(sElemId)) ? JSAF.getElementByName(sElemId, oCtxElem) : document.getElementById(sElemId));
  var nod = null;
  var elm = null;
  var nodAry = new Array();
  nodAry.push(oCtxElem);
  while (nodAry.length > 0) {
    nod = nodAry.shift();
    for (var i = 0; i < nod.childNodes.length; ++i) {
      elm = nod.childNodes[i];
      if (elm.nodeType == 1) {
        if (elm.getAttribute("id") == sElemId) { nodAry = null; return(elm); }
        else nodAry.push(elm);
      }
    }
  }
  nodAry = null;
  return(null);
}
/** get a collection of elements with the NAME attribute of sElemName.
 * @param sElemName the element name attribute string
 * @param oCtxElem (optional) the context element. if it is null (or is is not provided) then it is document.
 * @return always an Array (the length could be 0 if there are no elements)
 */
JSAF.getElementsByName = function(sElemName, oCtxElem) {
  var elms = null;
  if (oCtxElem == null) {
    elms = document.getElementsByName(sElemName);
  } else {
    elms = new Array();
    var nod = null;
    var elm = null;
    var nodAry = new Array();
    nodAry.push(oCtxElem);
    while (nodAry.length > 0) {
      nod = nodAry.shift();
      for (var i = 0; i < nod.childNodes.length; ++i) {
        elm = nod.childNodes[i];
        if (elm.nodeType == 1) {
          nodAry.push(elm);
          if (elm.getAttribute("name") == sElemName) {
        	  elms.push(elm);
          } else if (elm.getAttribute("id") == sElemName) {
        	  elms.push(elm);
          }
        }
      }
    }
    nodAry = null;
  }
  if (typeof(elms.length) == "undefined") { 
    var elms1 = new Array();
    elms1[0] = elms;
    elms = elms1;
  }
  return(elms);
}
/** get a element with the NAME attribute of sElemName.
 * @param sElemName the element name attribute string
 * @param oCtxElem (optional) the context element. if it is null (or is is not provided) then it is document.
 * @return object or null if not found
 */
JSAF.getElementByName = function(sElemName, oCtxElem) {
	var elements = JSAF.getElementsByName(sElemName, oCtxElem);
	if (elements != null && elements.length > 0) {
		return elements[0];
	} else if (!JSAF.isObjectUndefined(document.getElementById(sElemName))) {
		if (oCtxElem == null) return document.getElementById(sElemName);
		  var nod = null;
		  var elm = null;
		  var nodAry = new Array();
		  nodAry.push(oCtxElem);
		  while (nodAry.length > 0) {
		    nod = nodAry.shift();
		    for (var i = 0; i < nod.childNodes.length; ++i) {
		      elm = nod.childNodes[i];
		      if (elm.nodeType == 1) {
		        if (elm.getAttribute("id") == sElemName) { nodAry = null; return(elm); }
		        else nodAry.push(elm);
		      }
		    }
		  }
		  nodAry = null;
	} else {
		return null;
	}
}
/** get get Iframe contentWindow that to invoke the method in the iframe
 * @param iFrameObj the Object of iframe.
 * @return iFrame contentWindow
 */
JSAF.getIFrameContentWindow = function(iFrameObj) {
	var iFrameContentWindow;
    if (iFrameObj.contentWindow)
    	iFrameContentWindow = iFrameObj.contentWindow;
    else if (iFrameObj.window)
    	iFrameContentWindow = iFrameObj.window;
    else 
    	iFrameContentWindow = iFrameObj;
    return iFrameContentWindow;
}
/** get get Iframe document
 * @param iFrameObj the Object of iframe.
 * @return iFrame Document or null if not found.
 */
JSAF.getIFrameDocument = function(iFrameObj) {
	try {
		var iFrameDocument;
	    if (iFrameObj.contentDocument) // For fireFox,google.
	    	iFrameDocument = iFrameObj.contentDocument;
	    else if (iFrameObj.contentWindow)   // For IE5.5 and IE6
	    	iFrameDocument = iFrameObj.contentWindow.document;
	    else if (iFrameObj.document)    // For IE5
	    	iFrameDocument = iFrameObj.document;
	    return iFrameDocument;
	} catch (e){}
}
/** get elements with the NAME attribute of sElemName in the iframe.
 * @param iFrameObj the Object of iframe.
 * @param sNameInIFrame the element name attribute string in the iframe.
 * @return elements or null if not found.
 */
JSAF.getIFrameElementsByName = function(iFrameObj, sNameInIFrame) {
	return JSAF.getIFrameDocument(iFrameObj).getElementsByName(sNameInIFrame);
}
/** get a element with the NAME attribute of sElemName in the iframe.
 * @param iFrameObj the Object of iframe.
 * @param sNameInIFrame the element name attribute string in the iframe.
 * @return object or null if not found.
 */
JSAF.getIFrameElementByName = function(iFrameObj, sNameInIFrame) {
	return JSAF.getIFrameElementsByName(iFrameObj, sNameInIFrame)[0];
}
/** get the forms in the iframe.
 * @param iFrameObj the Object of iframe.
 * @return forms or null if not found.
 */
JSAF.getIFrameForms = function(iFrameObj) {
    return JSAF.getIFrameDocument(iFrameObj).getElementsByTagName("form");
}
/** get a collection of child-elements with the tag name of sTagName.
 * @param sTagName the element tag name attribute string
 * @param oCtxElem (optional) the context element. if it is null (or it is not provided) then it is document.
 * @return always an Array (the length could be 0 if there are no elements)
 */
JSAF.getElementsByTagName = function(sTagName, oCtxElem) {
  if (oCtxElem == null) oCtxElem = document;
  var elms = oCtxElem.getElementsByTagName(sTagName);
  if (typeof(elms.length) == "undefined") {
    var elms1 = new Array();
    elms1[0] = elms;
    elms = elms1;
  }
  return(elms);
}
/** disable user interactions on a given element and elements contained within this element.
 * @param oCtxElem the element. if it is not provided, then it is window.document.
 * @return arry of elements that are already disabled before this call. 
*/
JSAF.disableUserAction = function(oCtxElem) {
  return(JSAF.enableDisableUserAction(oCtxElem == null ? window.document : oCtxElem, false, null));
}
/** enable user interactions on a given element and elements contained within this element.
 * @param oCtxElem the element. if it is not provided, then it is window.document.
 * @param aryOrgDisabledElms array of elements to keep disabled. it is optional.
 * @return arry of elements that are already enabled before this call. 
*/
JSAF.enableUserAction = function(oCtxElem, aryOrgDisabledElms) {
  return(JSAF.enableDisableUserAction(oCtxElem, true, aryOrgDisabledElms));
}
/** enable/disable user interactions on a given element and elements contained within this element.
 * @param oCtxElem the element
 * @param bEnable true or false to enable or disable user interactions
 * @param aryReservedElms array of elements to keep unchanged.
 * @return arry of elements that are already enabled(if bEnable=true) or disabled(if bEnable=false) before this call. 
*/
JSAF.enableDisableUserAction = function(oCtxElem, bEnable, aryReservedElms) {
	var orgElms = new Array();
    var elms = JSAF.getElementsByTagName("INPUT", oCtxElem);
    if (elms.length > 0) for (var i = 0; i < elms.length; ++i) if (elms[i].type != "file") { if (elms[i].disabled == bEnable) { elms[i].disabled = (!bEnable); } else { orgElms[orgElms.length] = elms[i]; }}
    elms = JSAF.getElementsByTagName("BUTTON", oCtxElem);
    if (elms.length > 0) for (var i = 0; i < elms.length; ++i) if (elms[i].disabled == bEnable) { elms[i].disabled = (!bEnable); } else { orgElms[orgElms.length] = elms[i]; }
    var elms = JSAF.getElementsByTagName("SELECT", oCtxElem);
    if (elms.length > 0) for (var i = 0; i < elms.length; ++i) if (elms[i].disabled == bEnable) { elms[i].disabled = (!bEnable); } else { orgElms[orgElms.length] = elms[i]; }
    var elms = JSAF.getElementsByTagName("TEXTAREA", oCtxElem);
    if (elms.length > 0) for (var i = 0; i < elms.length; ++i) if (elms[i].disabled == bEnable) { elms[i].disabled = (!bEnable); } else { orgElms[orgElms.length] = elms[i]; }
    elms = JSAF.getElementsByTagName("A", oCtxElem);  // for <a> link, just disable/enable tab index
    if (elms.length > 0) {
      if (bEnable) {
        for (var i = 0; i < elms.length; ++i) if (elms[i].tabIndex < 0) { elms[i].tabIndex = (-1 - elms[i].tabIndex); } else { orgElms[orgElms.length] = elms[i]; }
      } else {
        for (var i = 0; i < elms.length; ++i) if (elms[i].tabIndex >= 0) { elms[i].tabIndex = (-1 - elms[i].tabIndex); } else { orgElms[orgElms.length] = elms[i]; }
      }
    }
    var frms = new Array();
    var frms2 = JSAF.getElementsByTagName("IFRAME", oCtxElem);
    if (frms2.length > 0) for (var j = 0; j < frms2.length; ++j) frms[frms.length] = frms2[j];
    // do not consider first level FRAME for now.
    //frms2 = JSAF.getElementsByTagName("FRAME", oCtxElem);
    //if (frms2.length > 0) for (var j = 0; j < frms2.length; ++j) frms[frms.length] = frms2[j];
    if (frms.length > 0) {
      var frmDoc = null;
      for (var k = 0; k < frms.length; ++k) {
        frmDoc = JSAF.getIFrameDocument(frms[k]);
        if(frmDoc) {
	    	 elms = JSAF.getElementsByTagName("INPUT", frmDoc);
	         if (elms.length > 0) for (var i = 0; i < elms.length; ++i) if (elms[i].type != "file") {if (elms[i].disabled == bEnable) { elms[i].disabled = (!bEnable); } else { orgElms[orgElms.length] = elms[i]; }}
	         elms = JSAF.getElementsByTagName("BUTTON", frmDoc);
	         if (elms.length > 0) for (var i = 0; i < elms.length; ++i) if (elms[i].disabled == bEnable) { elms[i].disabled = (!bEnable); } else { orgElms[orgElms.length] = elms[i]; }
	         var elms = JSAF.getElementsByTagName("SELECT", frmDoc);
	         if (elms.length > 0) for (var i = 0; i < elms.length; ++i) if (elms[i].disabled == bEnable) { elms[i].disabled = (!bEnable); } else { orgElms[orgElms.length] = elms[i]; }
	         var elms = JSAF.getElementsByTagName("TEXTAREA", frmDoc);
	         if (elms.length > 0) for (var i = 0; i < elms.length; ++i) if (elms[i].disabled == bEnable) { elms[i].disabled = (!bEnable); } else { orgElms[orgElms.length] = elms[i]; }
	         elms = JSAF.getElementsByTagName("A", oCtxElem);  // for <a> link, just disable/enable tab index
             if (elms.length > 0) {
              if (bEnable) {
               for (var i = 0; i < elms.length; ++i) if (elms[i].tabIndex < 0) { elms[i].tabIndex = (-1 - elms[i].tabIndex); } else { orgElms[orgElms.length] = elms[i]; }
              } else {
               for (var i = 0; i < elms.length; ++i) if (elms[i].tabIndex >= 0) { elms[i].tabIndex = (-1 - elms[i].tabIndex); } else { orgElms[orgElms.length] = elms[i]; }
              }
             }
	         frms2 = JSAF.getElementsByTagName("IFRAME", frmDoc);
	         if (frms2.length > 0) for (var j = 0; j < frms2.length; ++j) frms[frms.length] = frms2[j];
	         frms2 = JSAF.getElementsByTagName("FRAME", frmDoc);
	         if (frms2.length > 0) for (var j = 0; j < frms2.length; ++j) frms[frms.length] = frms2[j];
        }
      }
    }
    if (orgElms.length == 0) orgElms = null;
    if (aryReservedElms != null && aryReservedElms.length > 0) {
      for (var i = 0; i < aryReservedElms.length; ++i) {
        if (aryReservedElms[i].tagName == "A") aryReservedElms[i].tabIndex = (-1 - aryReservedElms[i].tabIndex);
        else aryReservedElms[i].disabled = bEnable;
      }
    }
  return(orgElms);
}
/** set inner text of an element such as <li>, <p>, <td>, etc.
 * @param oElem the element object whose inner text to be set
 * @param sText the text string
 */
JSAF.setInnerText = function(oElem, sText) {
  if (JSAF.isBrowserIEInCompMode()) {
	  oElem.innerText = sText;
  } else {
	  var nds = oElem.childNodes;
	  for (var i = 0; i < nds.length; ++i) {
	    if (nds[i].nodeType == 3) { // 3 for Text Node, 1 for Element Node
	      nds[i].nodeValue = sText;
	      return;
	    }
	  }
	  oElem.appendChild(document.createTextNode(sText));
  }
}
/** get inner text of an element such as <li>, <p>, <td>, etc.
 * @param oElem the element object whose inner text to be retrieved
 * @return the text string
 */
JSAF.getInnerText = function(oElem) {
  var nds = oElem.childNodes;
  for (var i = 0; i < nds.length; ++i) {
    if (nds[i].nodeType == 3) { // 3 for Text Node, 1 for Element Node
      return(nds[i].nodeValue);
    }
  }
  return("");
}
/** get inner HTML of an element. Inner HTML presents all the elements the element contains in HTML string.
 * @param oElem the element object whose inner HTML to be retrieved
 * @return the inner HTML string
 */
JSAF.getInnerHTML = function(oElem) {
	if(!JSAF.isBrowserIEInCompMode()) {
		var objElems  = JSAF.getElementsByTagName("input", oElem);
		for(var i = 0; i < objElems.length; i++) {
			if ((objElems[i].type == "checkbox" || objElems[i].type == "radio") && objElems[i].checked) {
					objElems[i].setAttribute("checked", "checked");
		     	} else if((objElems[i].type == "checkbox" || objElems[i].type == "radio") && !objElems[i].checked) {
					objElems[i].removeAttribute("checked", 0);
		     	} else {
		     		objElems[i].setAttribute("value", objElems[i].value);
		        }
		}
		objElems  = JSAF.getElementsByTagName("TEXTAREA", oElem);
		for(var i = 0; i < objElems.length; i++) {
			JSAF.setInnerText(objElems[i], objElems[i].value);
		}
		objElems  = JSAF.getElementsByTagName("SELECT", oElem);
		for(var i = 0; i < objElems.length; i++) {
			var iSelectLen = objElems[i].options.length;
			for(var j = 0; j < iSelectLen; j++) {
			 	if(objElems[i].options[j].value == objElems[i].value) {
			 		objElems[i].options[j].setAttribute("selected", "selected");
			 	} else {
			 		objElems[i].options[j].removeAttribute("selected", 0);
			 	}
			}
		}
	}
	return(oElem.innerHTML);
}
/** set inner HTML of an element
 * @param oElem the element object whose inner HTML to be set
 * @param sHtml the HTML string (which could contain tags)
 */
JSAF.setInnerHTML = function(oElem, sHtml) {
  oElem.innerHTML = sHtml;
}
/** get outer HTML of an element. Outer HTML presents the element and all elements it contains in HTML String.
 * @param oElem the element object whose outer HTML to be retrieved
 * @return the outer HTML string
 */
JSAF.getOuterHTML = function(oElem) {
  // as of this writing, IE, Safari, Chrome and Opera support elem.outerHTML. FF doesn't but it will support later.
  if (oElem.outerHTML) return(oElem.outerHTML);
  else {
    var div = document.createElement("div");
    div.appendChild(oElem.cloneNode(true));
    var sHtml = JSAF.getInnerHTML(div);
    div = null;
    return(sHtml);
  }
}
/** set outer HTML of an element
 * @param oElem the element object whose outer HTML to be set
 * @param sHtml the HTML string
 */
JSAF.setOuterHTML = function(oElem, sHtml) {
  if (oElem.outerHTML) oElem.outerHTML = sHtml;
  else {
    // TODO: 
  }
}
/** copy an element and its attributes and all elements it contains.
 * @param oElem the element object to be copied
 * @return new element object
 */
JSAF.cloneElement = function(oElem) {
  return(oElem.cloneNode(true));
}
/** set tooltip text of an element. for IMG don't use alt attribute.
 * @param oElem the element
 * @param sTip string text
 */
JSAF.setElementTooltip = function(oElem, sTip) {
	oElem.title = sTip;
}
/** get tooltip of an element
 */
JSAF.getElementTooltip = function(oElem) {
	return(oElem.title);
}
/** show HTML Element
 * @param oElem is the element object to be shown
 */
JSAF.showElement = function(oElem) {
  if (!oElem.style.display || oElem.style.display != "inline") oElem.style.display = "inline";
}
/** hide HTML Element
 * @param oElem is the element object to be hidden
 */
JSAF.hideElement = function(oElem) {
  if (!oElem.style.display || oElem.style.display != "none") oElem.style.display = "none";
}
/** get the absolute position (relative to the top-left corner of the page) of the element top-left corner
 * @param oElem is the element object
 * @return object with { x : pos_x,  y : pos_y } in pixels. use retObj.x and retObj.y to access x and y.
 */
JSAF.elementPagePosition = function(oElem) {
  var x = 0, y = 0;
  while (oElem.offsetParent) {
    x += oElem.offsetLeft;
    y += oElem.offsetTop;
  	oElem = oElem.offsetParent;
  }
  return({ x : x, y : y });
}
/** get the absolute X position (relative to the top-left corner of the page) of the element top-left corner
 * @param oElem is the element object
 * @return integer number of X in pixels
 */
JSAF.elementPageX = function(oElem) {
  var x = 0;
  while (oElem.offsetParent) {
    x += oElem.offsetLeft;
  	oElem = oElem.offsetParent;
  }
  return(x);
}
/** get the absolute Y position (relative to the top-left corner of the page) of the element top-left corner
 * @param oElem is the element object
 * @return integer number of Y in pixels
 */
JSAF.elementPageY = function(oElem) {
  var y = 0;
  while (oElem.offsetParent) {
    y += oElem.offsetTop;
  	oElem = oElem.offsetParent;
  }
  return(y);
}
/** get current computed effective style object of a HTML Element
 * it merges all settings: 1. inline styles, 2. stylesheet rules, 3. attributes on html tags, 4. intrinsic definition of the html tag
 * @param oElem is the element object
 * @return the style object 
 */
JSAF.getComputedStyle = function(oElem) {
  if (JSAF.isBrowserIEInCompMode()) return(oElem.currentStyle);
  else return(document.defaultView.getComputedStyle(oElem, null)); // or window.getComputedStyle(oElem, null)
}
/** set stylesheet classes to HTML Element
 * @param oElem is the element object
 * @param sClazzes the stylesheet class names separated by space
 */
JSAF.setStyleClass = function(oElem, sClazzes) {
  if (JSAF.isBrowserIEInCompMode()) oElem.setAttribute("className", sClazzes);
  else oElem.setAttribute("class", sClazzes);
}
/** add stylesheet class to HTML Element
 * @param oElem is the element object
 * @param sClazz the stylesheet class name
 */
JSAF.addStyleClass = function(oElem, sClazz) {
  var clazzes = JSAF.getStyleClass(oElem);
  if (clazzes == null) clazzes = sClazz;
  else clazzes += " " + sClazz;
  JSAF.setStyleClass(oElem, clazzes);
}
/** remove stylesheet class from HTML Element
 * @param oElem is the element object
 * @param sClazz (optional) the style class name to be removed. if it is null or not provided, then remove all classes.
 */
JSAF.removeStyleClass = function(oElem, sClazz) {
  if (sClazz == null) {
    if (JSAF.isBrowserIEInCompMode()) oElem.removeAttribute("className", 0);
    else oElem.removeAttribute("class", 0);
  } else {
    var clazzes = JSAF.getStyleClass(oElem);
    if (clazzes == null) return;
    clazzes = (clazzes + " ").replace(sClazz + " ", "");
    while (clazzes.charAt(0) == " " ) clazzes = clazzes.substring(1, clazzes.length);
    while (clazzes.charAt(clazzes.length - 1) == " " ) clazzes = clazzes.substring(0, clazzes.length - 1);
    if (clazzes.length == 0) {
      if (JSAF.isBrowserIEInCompMode()) oElem.removeAttribute("className", 0);
      else oElem.removeAttribute("class", 0);
    } else {
      JSAF.setStyleClass(oElem, clazzes);
    }
  }
}
/** get stylesheet class name of HTML Element.
 * @param oElem is the element object
 * @return the stylesheet class name or null if it is not set
 */
JSAF.getStyleClass = function(oElem) {
  if (JSAF.isBrowserIEInCompMode()) return(oElem.getAttribute("className"));
  else return(oElem.getAttribute("class"));
}
/** check whether HTML Element has the given stylesheet class 
 * @param oElem is the element object
 * @param sClazz the stylesheet class name to check
 */
JSAF.hasStyleClass = function(oElem, sClazz) {
  var clazzes = JSAF.getStyleClass(oElem);
  if (clazzes) return((clazzes + " ").indexOf(sClazz + " ") >= 0);
  else return(false);
}
/** get style float property of HTML Element
 * @param oElem is the element object
 * @return the string value of style float, "left" or "right" or "none"
 */
JSAF.getStyleFloat = function(oElem) {
  if (oElem.style.styleFloat != null) return(oElem.style.styleFloat);  // IE
  else if (oElem.style.cssFloat != null) return(oElem.style.cssFloat);
  else return("");
}
/** set style float property to HTML Element
 * @param oElem is the element object
 * @param sFloatDir the value of style float, "left" or "right" or "none"
 */
JSAF.setStyleFloat = function(oElem, sFloatDir) {
  if (oElem.style.styleFloat != null) oElem.style.styleFloat = sFloatDir; // IE
  else if (oElem.style.cssFloat != null) oElem.style.cssFloat = sFloatDir;
}

/** get value of attribute "for" of element
 * @param oElem is the element object
 * @return the string value of the "for" attribute
 */
JSAF.getAttributeFor = function(oElem) {
  var fv = oElem.getAttribute("for");
  if (fv == null) fv = oElem.getAttribute("htmlFor");  // IE
  return(fv);
}
/** set value of attribute "for" of element
 * @param oElem is the element object
 * @param sValue the value of the "for" attribute
 */
JSAF.setAttributeFor = function(oElem, sValue) {
  if (oElem.getAttribute("for") == null) oElem.setAttribute("htmlFor", sValue);  // IE
  else oElem.setAttribute("for", sValue);
}

/** get mouse X position relative to the top-left corner of the page of the browser window. Page is the whole content area which could be bigger than the view (scrolled)
 * @param oEvent the event 
 * @return integer number of the X in pixels
 */
JSAF.mousePageX = function(oEvent) {
  if (!oEvent) oEvent = window.event;
  if (oEvent.pageX) {
  	if (JSAF.isBrowserIEInCompMode() && oEvent.type == "mousemove") {
  		return (oEvent.clientX + window.document.body.scrollLeft);
    } else {
    	return(oEvent.pageX);
    }
  }// Non-IE
  else {
    return(oEvent.clientX + window.document.body.scrollLeft);
  }  // IE
}
/** get mouse Y position relative to the top-left corner of the page of the browser window.
 * @param oEvent the event 
 * @return integer number of the Y in pixels
 */
JSAF.mousePageY = function(oEvent) {
  if (!oEvent) oEvent = window.event;
  if (oEvent.pageY) {
  	if (JSAF.isBrowserIEInCompMode() && oEvent.type == "mousemove") {
	    return(oEvent.clientY + window.document.body.scrollTop);
    } else {
  		return(oEvent.pageY);
  	}
  }  // Non-IE
  else {
  	return(oEvent.clientY + window.document.body.scrollTop);
  }  // IE
}
/** get mouse X position relative to the top-left corner of client view area of the browser window. View is the visible area which could be scrolled to view different part of the page.
 * @param oEvent the event 
 * @return integer number of the X in pixels
 */
JSAF.mouseViewX = function(oEvent) {
  if (!oEvent) oEvent = window.event;
  if (oEvent.pageX) return(oEvent.pageX - window.document.body.scrollLeft);  // Non-IE
  else return(oEvent.clientX);  // IE
}
/** get mouse Y position relative to the top-left corner of client view area of the browser window.
 * @param oEvent the event 
 * @return integer number of the Y in pixels
 */
JSAF.mouseViewY = function(oEvent) {
  if (!oEvent) oEvent = window.event;
  if (oEvent.pageY) return(oEvent.pageY - window.document.body.scrollTop);  // Non-IE
  else return(oEvent.clientY);  // IE
}
/** get mouse X offset position relative to the top-left corner of the element that fires the event. becasue evt.offsetXY/layerXY has different value for positioned and non-positioned element, so here use different way to implement it.
 * @param oEvent the event 
 * @return integer number of the X in pixels
 */
JSAF.mouseOffsetX = function(oEvent) {
  if (!oEvent) oEvent = window.event;
  return(JSAF.mousePageX(oEvent) - JSAF.elementPageX(JSAF.eventSource(oEvent)));
}
/** get mouse Y offset position relative to the top-left corner of the element that fires the event
 * @param oEvent the event 
 * @return integer number of the Y in pixels
 */
JSAF.mouseOffsetY = function(oEvent) {
  if (!oEvent) oEvent = window.event;
  return(JSAF.mousePageY(oEvent) - JSAF.elementPageY(JSAF.eventSource(oEvent)));
}
/** get mouse X position relative to the top-left corner of the screen
 * @param oEvent the event 
 * @return integer number of the X in pixels
 */
JSAF.mouseScreenX = function(oEvent) {
  if (!oEvent) oEvent = window.event;
  return(oEvent.screenX);
}
/** get mouse Y position relative to the top-left corner of the screen
 * @param oEvent the event 
 * @return integer number of the Y in pixels
 */
JSAF.mouseScreenY = function(oEvent) {
  if (!oEvent) oEvent = window.event;
  return(oEvent.screenY);
}
/** get the view area X postion relative to the top-left corner of the page. It is the pixels the horizontal scrool-bar scrolled.
 * @return integer number of the X in pixels
 */
JSAF.windowViewX = function() {
  return(window.document.body.scrollLeft);
}
/** get the view area Y postion relative to the top-left corner of the page. It is the pixels the vertical scrool-bar scrolled.
 * @return integer number of the Y in pixels
 */
JSAF.windowViewY = function() {
  return(window.document.body.scrollTop);
}
/** get the width of the client view area of the browser window (not including margin, border, or scroll bar)
 * @return integer number of the width in pixels
 */
JSAF.windowViewWidth = function() {
  return(window.document.body.clientWidth);
}
/** get the height of the client view area of the browser window (not including margin, border, or scroll bar)
 * @return integer number of the height in pixels
 */
JSAF.windowViewHeight = function() {
  return(window.document.body.clientHeight);
}
/** get the width of the page content of the browser window (it includes the content outside of the current view to be scrolled)
 * @return integer number of the width in pixels
 */
JSAF.windowPageWidth = function() {
  return(window.document.body.clientWidth > window.document.body.scrollWidth ? window.document.body.clientWidth : window.document.body.scrollWidth);
}
/** get the height of the page content of the browser window (it includes the content outside of the current view to be scrolled)
 * @return integer number of the width in pixels
 */
JSAF.windowPageHeight = function() {
  return(window.document.body.clientHeight > window.document.body.scrollHeight ? window.document.body.clientHeight : window.document.body.scrollHeight);
}
/** adjust a box position based on the window view area to make the box area visible as much as possible within the view
 * @param x, y is the absolute position of the top-left corner of the box area
 * @param w, h the width and height of the box area
 * @return adjusted (x, y) in object { x : pos_x, y : pos_y }
 */
JSAF.boxAdjustPosition = function(x, y, w, h) {
  var vwX = JSAF.windowViewX(), vwY = JSAF.windowViewY();
  var vwW = JSAF.windowViewWidth(),  vwH = JSAF.windowViewHeight();
  if ((x + w) > (vwX + vwW)) x -= ((x + w) - (vwX + vwW)); 
  if (x < vwX) x = vwX;
  if ((y + h) > (vwY + vwH)) y -= ((y + h) - (vwY + vwH)); 
  if (y < vwY) y = vwY;
  return({ x : x, y : y });
}
/** adjust a box position to put it at center of the window
 * @param w, h the width and height of the box
 * @return top-left corner absolute position (x, y) of the box in object { x : pos_x, y : pos_y }
 */
JSAF.boxCenterPosition = function(w, h) {
  var vwX = JSAF.windowViewX(), vwY = JSAF.windowViewY();
  var vwW = JSAF.windowViewWidth(),  vwH = JSAF.windowViewHeight();
  var x = (vwW > w ? (vwX + (vwW - w)/2) : vwX);
  var y = (vwH > h ? (vwY + (vwH - h)/2) : vwY);
  return({ x : x, y : y });
}
/** align a box so that either the top and bottom is on the given alignY
 * @param y the box Y position of the top-left corner
 * @param h the box height
 * @param alignY the align Y position
 * @return adjusted Y postion
 */
JSAF.boxAlignVertical = function(y, h, alignY) {
  var vwY = JSAF.windowViewY();
  var vwH = JSAF.windowViewHeight();
  if (y < alignY) {
    y = alignY - h;
    if (y < vwY) y = alignY;
  } else {
    y = alignY;
    if ((y + h > vwY + vwH) && (alignY - vwY > h)) y = alignY - h;
  }
  return(y);
}
/** align a box so that either the left and right is on the given alignX
 * @param x the box X position of the top-left corner
 * @param w the box width
 * @param alignX the align X position
 * @return adjusted X postion
 */
JSAF.boxAlignHorizontal = function(x, w, alignX) {
  var vwX = JSAF.windowViewX();
  var vwW = JSAF.windowViewWidth();
  if (x < alignX) {
    x = alignX - w;
    if (x < vwX) x = alignX;
  } else {
    x = alignX;
    if ((x + w > vwX + vwW) && (alignX - vwX > w)) x = alignX - w;
  }
  return(x);
}
/** get the available screen width in pixels
 * @return integer number of available width of screen
 */
JSAF.screenWidth = function() {
  return(window.screen.availWidth);
}
/** get the available screen height in pixels
 * @return integer number of available height of screen
 */
JSAF.screenHeight = function() {
  return(window.screen.availHeight);
}
/** get the collection of direct child HTML Elements and Text Nodes of a given element
 * @param oElem is the element object
 * @return always array of elements and text nodes.
 */
JSAF.childNodes = function(oElem) {
  return(oElem.childNodes);
}
/** get the collection of direct child HTML Elements of a given element
 * @param oElem is the element object
 * @return always array of elements.
 */
JSAF.childElements = function(oElem) {
  var elms = new Array();
  var nds = oElem.childNodes;
  for (var i = 0; i < nds.length; ++i) {
    if (nds[i].nodeType == 1) elms.push(nds[i]);
  }
  return(elms);
}
/** fix event object for multiple browsers
 * @param evt the original event object
 * @return event
 */
JSAF.fixEvent = function(evt) {
  if (typeof evt == "undefined") { evt = window.event; }
  if (typeof evt.layerX == "undefined") { evt.layerX = evt.offsetX; }
  if (typeof evt.layerY ==  "undefined") { evt.layerY = evt.offsetY; }
  if (typeof evt.which ==  "undefined") { evt.which = evt.button; }
  return(evt);
}
/** add event listener to HTML Element
 * @param oElem is the element object
 * @param sType the event type, such as click, change, etc.
 * @param funHandler event handler being called
 */
JSAF.addEventListener = function(oElem, sType, funHandler) {
  if (oElem.addEventListener) {
    oElem.addEventListener(sType, funHandler, true);
  } else if (oElem.attachEvent) {
    oElem.attachEvent("on" + sType, function() { funHandler.apply(oElem, arguments); });
  }
}
/** fires a specified event on the object.
 * @param oElem is the element object
 * @param sType the event type, such as click, change, etc.
 */
JSAF.fireEvent = function(oElem, sType){
	if (document.createEventObject){
		// IE
        var evt = document.createEventObject();
        return oElem.fireEvent('on'+sType, evt)
    }
    else{
        // other browser support dispatchEvent
        var evt = document.createEvent('HTMLEvents');
        evt.initEvent(sType, true, true); 
        return !oElem.dispatchEvent(evt);
    }
}
/** remove event listener to HTML Element
 * @param oElem is the element object
 * @param sType the event type, such as click, change, etc.
 * @param funHandler event handler being called
 */
JSAF.removeEventListener = function(oElem, sType, funHandler) {
  if (oElem.removeEventListener) {
    oElem.removeEventListener(sType, funHandler, true);
  } else if (oElem.detachEvent ) {
    oElem.detachEvent("on" + sType, function() { funHandler.apply(oElem, arguments); });
  }
}
/** get the current event object within the event listener function
 * @return event object
 */
JSAF.currentEvent = function() {
  if (window.event) return(window.event);  // IE
  var fnCaller = arguments.callee.caller;
  while (fnCaller) {
    if (fnCaller.arguments.length > 0) {
      var obj = fnCaller.arguments[0];
      //Opera 12 returns a false negative for "KeyboardEvent" in window until you call document.createEvent( "KeyboardEvent" ). 
      if(JSAF.getBrowserName() != "Opera" || "KeyboardEvent" in window) {
    	  if (obj.constructor == MouseEvent || obj.constructor == Event || obj.constructor == KeyboardEvent || obj.constructor == FocusEvent ) return(obj);   
      } else {
    	  if (obj.constructor == MouseEvent || obj.constructor == Event || obj.constructor == FocusEvent) return(obj);
      }
    }
    fnCaller = fnCaller.arguments.callee.caller;
  }
  return(null);
}
/** get the element that triggered the event
 * @param oEvent the event 
 * @return element object
 */
JSAF.eventSource = function(oEvent) {
  if (oEvent.srcElement) return(oEvent.srcElement);
  else if (oEvent.target) return(oEvent.target);
  else return(null);
}
/** process any javascripts included in the given context element. For inline script, execute it; for included js file, load it and execute it.
 * This is used to dynamically run javascripts after an innerHTML is assigned and there are some scripts included in the innerHTML.
 * @param oCtxElem the element within which any javascripts are included
 * @param bAsync (optional) true (load script file in async mode), false (load script file in synch mode). default is true
 */
JSAF.processScripts = function(oCtxElem, bAsync) {
  if (oCtxElem == null) return;
  var jsElems = JSAF.getElementsByTagName("script", oCtxElem);
  if (jsElems != null && jsElems.length > 0) {
    for (var i = 0; i < jsElems.length; ++i) {
      if (jsElems[i].src == null || jsElems[i].src == "") JSAF.evalGlobal(jsElems[i].innerHTML);
      else JSAF.loadJsFile(jsElems[i].src, bAsync);
    }
  }
}
/** swap <img> src
 *    <img src="images/imga.gif" onmouseover="JSAF.imgSwap(this, 'images/imga.gif', 'images/imgb.gif', 12)" onmouseover="JSAF.imgSwap(this, 'images/imga.gif', 'images/imgb.gif', 12)"/>
 * @param oImgElem the <img> element object
 * @param sImg1 the first image name such as "images/imga.gif"
 * @param sImg2 the second image name such as "images/imgb.gif"
 * @param iLastLen (optional) the length of the chars from the end of the image names (sImg1 and sImg2) that can be used to differentiate these two images. If it is not provided, then the image names must be different.
 */
JSAF.imgSwap = function(oImgElem, sImg1, sImg2, iLastLen) {
  if (iLastLen == null) {
    if (oImgElem.src.substring(oImgElem.src.lastIndexOf("/") + 1) == sImg1.substring(sImg1.lastIndexOf("/") + 1)) oImgElem.src = sImg2;
    else oImgElem.src = sImg1;
  } else {
    if (oImgElem.src.substring(oImgElem.src.length - iLastLen) == sImg1.substring(sImg1.length - iLastLen)) oImgElem.src = sImg2;
    else oImgElem.src = sImg1;
  }
}
/** get redion buttons group selected value
 * @param radioInfo it could be radio button name string, or radio buttons element(s) (document.forms["myFrm"].elements["rbName"])
 * @param oFrm (optional) the form element object if radioInfo is the radio button string name
 * @return string value of the selected radio button
 */
JSAF.radioGroupGetValue = function(radioInfo, oFrm) {
  var elems = radioInfo;
  if (typeof(elems) == "string") elems = oFrm.elements[radioInfo];
  if (elems.checked != null) return(elems.value);
  if (elems.length != null) { for (var i = 0; i < elems.length; ++i) { if (elems[i].checked == true) return(elems[i].value); }}
  return("");
}
/** set redion buttons group selected value
 * @param radioInfo it could be radio button name string, or radio buttons element(s) (document.forms["myFrm"].elements["rbName"])
 * @param sValue string value of the radio button to be selected
 * @param oFrm (optional) the form element object if radioInfo is the radio button string name
 */
JSAF.radioGroupSetValue = function(radioInfo, sValue, oFrm) {
  var elems = radioInfo;
  if (typeof(elems) == "string") elems = oFrm.elements[radioInfo];
  if (elems.checked != null) {
    elems.checked = (sValue != null && elems.value == sValue) ? true : false;
  } else if (elems.length != null) {
    for (var i = 0; i < elems.length; ++i) {
      elems[i].checked = (sValue != null && elems[i].value == sValue) ? true : false;
    }
  }
}
/** get checkbox group selected values
 * @param checkboxInfo it could be checkbox name string, or checkbox element(s) (document.forms["myFrm"].elements["cbName"])
 * @param oFrm (optional) the form element object if checkboxInfo is the checkbox string name
 * @return array of string values of the selected checkboxes. It could be an empty array
 */
JSAF.checkboxGroupGetValues = function(checkboxInfo, oFrm) {
  var elems = checkboxInfo;
  if (typeof(elems) == "string") elems = oFrm.elements[checkboxInfo];
  var vals = new Array();
  if (elems.checked != null) {
    vals.push(elems.value);
  } else if (elems.length != null) {
    for (var i = 0; i < elems.length; ++i) { if (elems[i].checked == true) vals.push(elems[i].value); }
  }
  return(vals);
}
/** set checkbox group selected values
 * @param checkboxInfo it could be checkbox name string, or checkbox element(s) (document.forms["myFrm"].elements["cbName"])
 * @param aryValues array of string values of the selected checkboxes. It could be an empty array or null
 * @param oFrm (optional) the form element object if checkboxInfo is the checkbox string name
 */
JSAF.checkboxGroupSetValues = function(checkboxInfo, aryValues, oFrm) {
  var elems = checkboxInfo;
  if (typeof(elems) == "string") elems = oFrm.elements[checkboxInfo];
  if (typeof(aryValues) == "string") { var tmp = new Array(); tmp.push(aryValues); aryValues = tmp; }
  if (elems.checked != null) {
    if (aryValues != null) {
      var i = 0;
      while (i < aryValues.length) {
        if (aryValues[i] == elems.value) { elems.checked = true; break; }
        ++i;
      }
      if (i == aryValues.length) elems.checked = false;
    } else elems.checked = false;
  } else if (elems.length != null) {
    for (var j = 0; j < elems.length; ++j) {
      if (aryValues != null) {
        var i = 0;
        while (i < aryValues.length) {
          if (aryValues[i] == elems[j].value) { elems[j].checked = true; break; }
          ++i;
        }
        if (i == aryValues.length) elems[j].checked = false;
      } else elems[j].checked = false;
    }
  }
}
/** get dropdown box selected value. dropdown is <select size="1">
 * @param oDropdownElem the dropdown element object
 * @return string value
 */
JSAF.dropdownGetValue = function(oDropdownElem) {
  return(oDropdownElem.value);
}
/** set dropdown box selected value. dropdown is <select size="1">
 * @param oDropdownElem the dropdown element object
 * @param sValue the string value
 */
JSAF.dropdownSetValue = function(oDropdownElem, sValue) {
  for (var i = 0; i < oDropdownElem.length; ++i) {
    if (oDropdownElem[i].value == sValue) { oDropdownElem[i].selected = true;  return; }
  }
}
/** set dropdown box options. It removes any existing options and add the new options.
 * @param oDropdownElem the dropdown element object
 * @param aryOptions the option info in array [ { value: "val", text: "dispText", selected: true }, ... ]. "selected" is optional.
 */
JSAF.dropdownSetOptions = function(oDropdownElem, aryOptions) {
  JSAF.listboxSetOptions(oDropdownElem, aryOptions);
}
/** get list box selected values. list box is like <select size="5" multiple="multiple">
 * @param oListboxElem the list box element object
 * @return array of string values of the selected items in list. It could be an empty array
 */
JSAF.listboxGetValues = function(oListboxElem) {
  var vals = new Array();
  for (var i = 0; i < oListboxElem.length; ++i) {
    if (oListboxElem[i].selected == true) vals.push(oListboxElem[i].value);
  }
  return(vals);
}
/** set list box selected values. list box is like <select size="5" multiple="multiple">
 * @param oListboxElem the list box element object
 * @param aryValues array of string values of the selected items in list. It could be an empty array or null
 */
JSAF.listboxSetValues = function(oListboxElem, aryValues) {
  if (typeof(aryValues) == "string") { var tmp = new Array(); tmp.push(aryValues); aryValues = tmp; }
  for (var i = 0; i < oListboxElem.length; ++i) {
    if (aryValues != null) {
      var j = 0;
      while (j < aryValues.length) {
        if (oListboxElem[i].value == aryValues[j]) { oListboxElem[i].selected = true; break; }
        ++j;
      }
      if (j == aryValues.length) oListboxElem[i].selected = false;
    } else oListboxElem[i].selected = false;
  }
}
/** set list box options. It removes any existing options and add the new options.
 * @param oListboxElem the dropdown element object
 * @param aryOptions the option info in array [ { value: "val", text: "dispText", selected: true }, ... ]. "selected" is optional.
 */
JSAF.listboxSetOptions = function(oListboxElem, aryOptions) {
  for (var i = oListboxElem.length - 1; i >= 0; --i) oListboxElem.options[i] = null;
  if (aryOptions != null && aryOptions.length > 0) {
    for (var i = 0; i < aryOptions.length; ++i) {
      var opt = document.createElement("option");
      opt.value = aryOptions[i].value;
      opt.text = aryOptions[i].text;
      if (aryOptions[i].selected != null && aryOptions[i].selected == true) opt.selected = true;
      oListboxElem.options.add(opt);
    }
  }
}

/************************************************************************************* 
 * This is Web Button component to support Buttons toggle between normal and changed.
 * Following sample shows one Button
 *  <button id="btnSearch" class="appButton" type="button" onclick="doWork();" title="Search(ALT+R)"><span class="btnSearch"><label>Sea<u>r</u>ch</label></span></button>
 * Sample usage:
 *   JSAF.appButton("btnSearch").toChanged();
 * @param btnInfo the button info. it could be a string of button id, or properties object of following data. if only id is provided, then just switch button icon. 
 *   oBtnProp = {
 *     buttonId : "btnSearch",   // the button id
 *     btn : { text : "buttonText",          title : "buttonTitle" },         // for normal button 
 *     btc : { text : "changedButtonText",   title : "changedButtonTitle" }   // for changed button
 *   }
 * @return button control object which has following functions:
 *   btnCtrlObj {
 *      toNormal();     // function to change button to normal state
 *      toChanged();    // function to change button to changed state
 *      toggle();       // function to toggle the state to the other one
 *   }
 *************************************************************************************/
JSAF.appButton = function(btnInfo) {
  if (!btnInfo) throw new Error("Button Info is empty!");
  if (typeof(btnInfo) == "string") {
    return(JSAF.appButton({ buttonId : btnInfo}));
  }
  if (!btnInfo.buttonId) throw new Error("Button Id must be provided!");
  var btnCtrlObj = {
    btnProp : btnInfo,
    btnElement : JSAF.getElementById(btnInfo.buttonId),
    
    // change Button to the given type. sType button type: "btn", "btc", etc.
    changeTo : function(sType) {
      var spanElm = JSAF.getElementsByTagName("span", this.btnElement)[0];
      var oldClazz = JSAF.getStyleClass(spanElm); // the class name is btnXXXX or btcXXXX
      if (oldClazz.substring(0, 3) == sType) return;
      var newClazz = sType + oldClazz.substring(3, oldClazz.length);
      JSAF.removeStyleClass(spanElm);
      JSAF.setStyleClass(spanElm, newClazz);
      if (this.btnProp) {
        var prop = this.btnProp[sType];
        if (prop && prop.title) this.btnElement.title = prop.title;
        if (prop && prop.text) JSAF.setInnerHTML(JSAF.getElementsByTagName("label", spanElm)[0], prop.text);
      }
    },
    
    // make this Button back to normal
    toNormal : function() {
      this.changeTo(JSAF.BTN_TYPE_NORMAL);
    },
    
    // make this Button toggle to changed
    toChanged : function() {
      this.changeTo(JSAF.BTN_TYPE_CHANGED);
    },
    
    // toggle button state
    toggle : function() {
      var spanElm = JSAF.getElementsByTagName("span", this.btnElement)[0];
      var t = JSAF.getStyleClass(spanElm).substring(0, 3); // the class name is btnXXXX or btcXXXX
      if (t == JSAF.BTN_TYPE_NORMAL) this.toChanged();
      else this.toNormal();
    }
  }
  return(btnCtrlObj);
}
JSAF.BTN_TYPE_NORMAL = "btn";
JSAF.BTN_TYPE_CHANGED = "btc";

/************************************************************************************* 
 * This is Web Tab utility to support multiple Tab groups on same page and multiple-line Tab Group header. 
 * the selected Tab title's class name is 'appTabOn', and 'appTabOff' is for the deselected ones.
 * Following sample shows a tab group with 6 Tabs in two header lines.
 * <td id="tabGrpId0">  <!-- tab group -->
 * <table width="100%" border="0" cellspacing="0" cellpadding="0">
 *   <tr>
 *    <td> <!-- tab group header -->
 *     <table border="0" cellpadding="0" cellspacing="0"><tr>
 *         <td class="appTabOn" id="tabTabName1_h"><span><label>MyTab1</label></span></td>
 *         <td class="appTabOff" id="tabTabName2_h"><span><label>MyTab2</label></span></td>
 *         <td class="appTabOff" id="tabTabName3_h"><span><label>MyTab3</label></span></td></tr>
 *     </table>
 *     <table border="0" cellpadding="0" cellspacing="0"><tr>
 *         <td class="appTabOff" id="tabTabName4_h"><span><label>MyTab4</label></span></td>
 *         <td class="appTabOff" id="tabTabName5_h"><span><label>MyTab5</label></span></td>
 *         <td class="appTabOff" id="tabTabName6_h"><span><label>MyTab6</label></span></td></tr>
 *     </table>
 *    </td>
 *   </tr>
 *   <tr><td class="appTabBar"></td></tr>
 *   <tr>
 *    <td class="appTabPanelBorder"> <!-- tab group body -->
 *      <div id="tabTabName1" style="display:inline;"></div>
 *      <div id="tabTabName2" style="display:none;"></div>
 *      <div id="tabTabName3" style="display:none;"></div>
 *      <div id="tabTabName4" style="display:none;"></div>
 *      <div id="tabTabName5" style="display:none;"></div>
 *      <div id="tabTabName6" style="display:none;"></div>
 *    </td>
 *   </tr>
 * </table>
 * </td>
 * Sample script:
 *   var sampleTabGrp = JSAF.appTabGroup("tabGrpId0");
 * or:
 *   var sampleTabGrp = {
 *      tabGrpId : "tabGrpId0",
 *      onBeforeTabLeave : function(tabId) { .... },
 *      onTabEnter : function(tabId) { .... }
 *   }
 *   sampleTabGrp = JSAF.appTabGroup(sampleTabGrp);
 *
 * @since 2010-04-12
 * @update 2012-01-08
 * 
 * @param tabGrpInfo it could be the string id of the Tab group element, or object of tab group info as follows
 *      oTabGrpInfo = {
 *          tabGrpId : "tabGrpId",  // the tab group id. If tabGrpId is not provided, then headerId and bodyId must be provided
 *          headerId : "headerId",  // the tab group header id 
 *          bodyId   : "bodyId",    // the tab group body id
 *          onBeforeTabLeave :  function(tabId),  // (optional) called before leaving a tab. returns false then dont leave tab
 *          onTabLeave       :  function(tabId),  // (optional) called after leaving a tab.
 *          onBeforeTabEnter :  function(tabId),  // (optional) called before entering a tab. returns false then dont enter tab
 *          onTabEnter       :  function(tabId),  // (optional) called after entering a tab.
 *      }
 * @return object of tab group info as above.
 */
JSAF.appTabGroup = function(tabGrpInfo) {
  if (typeof(tabGrpInfo) == "string") {  // it is tab group id string
    return(JSAF.appTabGroup({ tabGrpId: tabGrpInfo }));
  }
  if (tabGrpInfo.headerId == null || tabGrpInfo.bodyId == null) {
    if (tabGrpInfo.tabGrpId == null) throw new Error("Either tabGrpId or headerId/bodyId must be provided!");
    // try to get the header/body id based on the default layout <td id="grpId"><table><tr><td id="headerId">...</td></tr>...<tr><td id="bodyId">...</td></tr></table></td>
    var grpElm = JSAF.getElementById(tabGrpInfo.tabGrpId);
    if (!grpElm) throw new Error("Tab group id (" + tabGrpInfo.tabGrpId + ") not found!");
    var tblElm = JSAF.getElementsByTagName("table", grpElm);
    if (!tblElm || tblElm.length == 0) throw new Error("The table element is missing!");
    tblElm = tblElm[0];
    if (!tblElm || tblElm.rows.length < 2) throw new Error("The table has less than 2 rows!");
    var hId = tblElm.rows[0].cells[0].getAttribute("id");
    if (!hId) { hId = tabGrpInfo.tabGrpId + "_header"; tblElm.rows[0].cells[0].setAttribute("id", hId); }
    tabGrpInfo.headerId = hId;
    tabGrpInfo.tabGrpHeader = tblElm.rows[0].cells[0];
    var bId = tblElm.rows[tblElm.rows.length - 1].cells[0].getAttribute("id");
    if (!bId) { bId = tabGrpInfo.tabGrpId + "_body"; tblElm.rows[tblElm.rows.length - 1].cells[0].setAttribute("id", bId); }
    tabGrpInfo.bodyId = bId;
    tabGrpInfo.tabGrpBody = tblElm.rows[tblElm.rows.length - 1].cells[0];
  } else {
    // the Tab Group header
    tabGrpInfo.tabGrpHeader = JSAF.getElementById(tabGrpInfo.headerId);
    // the Tab Group body
    tabGrpInfo.tabGrpBody = JSAF.getElementById(tabGrpInfo.bodyId);
  }
  // the current tab id string
  tabGrpInfo.currentTabId = null;
  var tdTitles = JSAF.getElementsByTagName("td", tabGrpInfo.tabGrpHeader);
  for (var i = 0; i < tdTitles.length; ++i) {
    var tdTitle = tdTitles[i];
    var tdId_h = tdTitle.getAttribute("id");
    if (JSAF.hasStyleClass(tdTitle, "appTabOn")) tabGrpInfo.currentTabId = tdId_h.substring(0, tdId_h.length - 2);
    // set "click" event handler for tab title
    var clickHandler = function() {
      var newTabTitle = newTabTitle = this;  // "this" is the clicked tdTitle
      if (JSAF.hasStyleClass(newTabTitle, "appTabOn")) return;  // it is current one already
      if (typeof(tabGrpInfo.onBeforeTabLeave) == "function") {
        if (tabGrpInfo.onBeforeTabLeave(tabGrpInfo.currentTabId) == false) return;
      }
      if (typeof(tabGrpInfo.onTabLeave) == "function") tabGrpInfo.onTabLeave(tabGrpInfo.currentTabId);
      // switch tab
      var tdId_h = newTabTitle.getAttribute("id");
      var tdId = tdId_h.substring(0, tdId_h.length - 2);
      if (typeof(tabGrpInfo.onBeforeTabEnter) == "function") {
        if (tabGrpInfo.onBeforeTabEnter(tdId) == false) return;
      }
      // switch title class
      var curTabTitle = JSAF.getElementById(tabGrpInfo.currentTabId + "_h", tabGrpInfo.tabGrpHeader);
      JSAF.removeStyleClass(curTabTitle, "appTabOn");
      JSAF.addStyleClass(curTabTitle, "appTabOff");
      JSAF.removeStyleClass(newTabTitle, "appTabOff");
      JSAF.addStyleClass(newTabTitle, "appTabOn");
      // switch body content
      JSAF.hideElement(JSAF.getElementById(tabGrpInfo.currentTabId, tabGrpInfo.tabGrpBody));
      JSAF.showElement(JSAF.getElementById(tdId, tabGrpInfo.tabGrpBody));
      tabGrpInfo.currentTabId = tdId;
      if (typeof(tabGrpInfo.onTabEnter) == "function") tabGrpInfo.onTabEnter(tdId);
    };
    JSAF.addEventListener(tdTitle, "click", clickHandler);
  }
  // event handlers
  /** triggered before leaving current tab
   * @param tabId string id of current tab
   * @return boolean. if true then leave the tab; if false then stays in current tab
   */
   if (!tabGrpInfo.onBeforeTabLeave) tabGrpInfo.onBeforeTabLeave = null;
  /** triggered after leaving current tab
   * @param tabId string id of current tab
   */
   if (!tabGrpInfo.onTabLeave) tabGrpInfo.onTabLeave = null;
   /** to be triggered before entering a new tab
    * @param tabId string id of the new tab
    * @return boolean. if true then enter the tab; if false then stays in current tab
    */
   if (!tabGrpInfo.onBeforeTabEnter) tabGrpInfo.onBeforeTabEnter = null;
   /** triggered after entering a new tab
    * @param tabId string id of the new tab
    */
   if (!tabGrpInfo.onTabEnter) tabGrpInfo.onTabEnter = null;
   
   return(tabGrpInfo);
}

/************************************************************************************* 
 * AJAX Utility
 ************************************************************************************/
// Provide the XMLHttpRequest class for IE 5.x, 6.x. Other browsers (including IE 7.x-8.x) ignore this when XMLHttpRequest is predefined
if (typeof(XMLHttpRequest) == "undefined") {
  XMLHttpRequest = function () {
    try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e1) {}
    try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (e2) {}
    try { return new ActiveXObject("Msxml2.XMLHTTP");     } catch (e3) {}
    throw new Error("This browser does not support XMLHttpRequest.");
  };
}
/**
 * get an Ajax Worker
 * AjaxWorker class is the wrapper to send Ajax request.
 *  var ajaxInfo = { 
 *     url: "a.do",
 *     requestData: [ { name: "parm1", value: "value1" }, { name: "parm2", value: "value2" } ],
 *     returnDataType : JSAF.AJAX_DATA_TYPE_JSON, 
 *     handler : function(objReturn) { ..... }
 *  }
 *  JSAF.getAjaxWorker(ajaxInfo).send();
 * 
 * @since 2010-11-06
 * @update 2012-01-08
 *
 * @param ajaxInfo the properties object as follows:
 *   ajaxInfo {
 *     url : url,               // (required) the server url string
 *     requestData : reqData,   // (optional) the request data to be sent to server. It could be String, array of {name: nam, value: val}, array of unsigned bytes, XML DOM object. null means no data to be sent.
 *     headers : namevalue,     // (optional) array of HTTP request headers in object {name: nam, value: val} or in string (name=value), such as "Content-Type=text/html; charset=UTF-8".
 *     handler : function,      // (optional) a function to handle the response. This function takes argument (objReturn, handlerParam). If there is no need to handle, then pass null.
 *     handlerParam : params,   // (optional) the argument object to be passed back to the handler.
 *     async : async,           // (optional) true/false whether this is asynchronized Ajax call. Default is true.
 *     method : method,         // (optional) the HTTP method used to open the connection. GET, POST or HEAD. if it is not provided, it will be GET (if requestData is null) or POST (if requestData is not null)
 *     returnDataType : resType // (optional) the return data type: JSAF.AJAX_DATA_TYPE_TEXT (Text string. default); JSAF.AJAX_DATA_TYPE_JSON (JSON object); JSAF.AJAX_DATA_TYPE_XML (XML DOM object); JSAF.AJAX_DATA_TYPE_BYTE (Binary bytes array)
 *   }
 * @return object of AjaxWorker with following functions in addition to the incoming props: (in the handler function "this" points to this returned object)
 *   AjaxWorker = {
 *     send();             // send request
 *     getResponseText();  // get response in text string
 *     getResponseXml();   // get response in XML DOM object
 *     getResponseJson();  // get response in JSON object 
 *     getResponseBody();  // get response in an array of unsigned bytes
 *     abort();            // cancel the current HTTP request
 *     getAllResponseHeaders();           // get a String the complete list of response headers. Each name/value pair is delimited by a carriage return/line feed (CR/LF) sequence
 *     getResponseHeader(strHeaderName);  // get a tring value of the specified response header
 *     getHttpResponseStatusCode();       // returns an integer of the http response status code such as 401, 200, etc.
 *     getHttpResponseStatusText();       // returns a String the http response status text.
 *     getReadyStateCode();    // returns an integer the ready state code such as 0 (Uninitialized), 1 (Open), 2 (Sent), 3 (Receiving), 4 (Loaded)
 *   }
 */
JSAF.AJAX_DATA_TYPE_TEXT = "text";
JSAF.AJAX_DATA_TYPE_JSON = "json";
JSAF.AJAX_DATA_TYPE_XML = "xml";
JSAF.AJAX_DATA_TYPE_BYTE = "byte";
JSAF.getAjaxWorker = function(ajaxInfo) {
  if (ajaxInfo == null || ajaxInfo.url == null) throw new Error("URL is required!");
  var reqData = ajaxInfo.requestData;
  var metd = ajaxInfo.method;
  var sUrl = ajaxInfo.url;
  if (reqData != null) {
    if (typeof(reqData) == "string") {
      reqData = encodeURI(reqData);
    } else if (JSAF.isArray(reqData)) {
      var dat = "";
      for (var i = 0; i < reqData.length; ++i) {
        if (i > 0) dat += "&";
        dat += encodeURI(reqData[i].name) + "=" + encodeURI(reqData[i].value);
      }
      reqData = dat;
    }
  }
  if (metd == null) metd = (reqData == null ? "GET" : "POST");
  if (metd != "POST" && reqData != null && typeof(reqData) == "string") {
    var iP = sUrl.lastIndexOf("?");
    sUrl += (iP >= 0 ? (iP < (sUrl.length - 1) ? "&" : "") : "?") + reqData;
  }
  var ajaxWorker = {
    url : sUrl,
    requestData : (metd == "POST" ? reqData : null),
    headers :  ajaxInfo.headers,
    handler : ajaxInfo.handler,
    handlerParam : ajaxInfo.handlerParam,
    async : ((ajaxInfo.async == null || ajaxInfo.async == true) ? true : false),
    method : metd,
    returnDataType : ajaxInfo.returnDataType,
    xmlHttpRequest : new XMLHttpRequest(),
    
    // method to send the Ajax reqeust to server and process the resposne using the handler
    send : function() {
      this.xmlHttpRequest.open(this.method, this.url, this.async);
      if (this.headers != null) {
        if(typeof(this.headers) == "string") {
  	    	this.headers = this.headers.split(";");
  	    }
        for (var i = 0; i < this.headers.length; ++i) {
          var nv = this.headers[i];
          if (typeof(nv) == "string") {
            var iPos = nv.indexOf("=");
            if (iPos > 0) {
              this.xmlHttpRequest.setRequestHeader(nv.substring(0, iPos).trim(), nv.substring(iPos + 1).trim());
            }
          } else {
              this.xmlHttpRequest.setRequestHeader(nv.name, nv.value);
          }
        }
      }
      if (this.requestData == null) {
        this.xmlHttpRequest.send();
      } else {
        this.xmlHttpRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        this.xmlHttpRequest.send(this.requestData);
      }
      if (!this.async && this.handler) this.handler.call(this, this.getResponseData(), this.handlerParam);
    },
    
    // Cancels the current HTTP request. It interrupts an asynchronous operation in progress.
    // Calling abort resets the object; the onreadystatechange event handler is removed, and readyState is changed to 0 (uninitialized).
    abort : function() {  this.xmlHttpRequest.abort(); },

    // returns a String the complete list of response headers. Each name/value pair is delimited by a carriage return/line feed (CR/LF) sequence
    getAllResponseHeaders : function() {  return(this.xmlHttpRequest.getAllResponseHeaders()); },
    
    // Returns a String value of the specified response header.
    getResponseHeader : function(strHeaderName) {  return(this.xmlHttpRequest.getResponseHeader(strHeaderName)); },
    
    // returns an integer the http response status code such as 401, 200, etc.
    getHttpResponseStatusCode : function() {  return(this.xmlHttpRequest.status); },

    // returns a String the http response status text.
    getHttpResponseStatusText : function() {  return(this.xmlHttpRequest.statusText); },

    // returns an integer the ready state code such as 0 (Uninitialized), 1 (Open), 2 (Sent), 3 (Receiving), 4 (Loaded)
    getReadyStateCode : function() {  return(this.xmlHttpRequest.readyState); },

    // returns an array of unsigned bytes of the response body data.
    getResponseBody : function() {  return(this.xmlHttpRequest.readyState == 4 ? this.xmlHttpRequest.responseBody : null); },

    // returns a String of the response body data.
    getResponseText : function() {  return(this.xmlHttpRequest.readyState == 4 ? this.xmlHttpRequest.responseText : null); },

    // returns an XML DOM object of the response body data.
    getResponseXml : function() {  return(this.xmlHttpRequest.readyState == 4 ? this.xmlHttpRequest.responseXML : null); },

    // returns a JavaScript Object of the response body data in JSON format { name1: value1, name2: value2, ... namen: valuen }
    getResponseJson : function() {  return(this.xmlHttpRequest.readyState == 4 ? JSON.parse(this.xmlHttpRequest.responseText) : null); },
    
    // return the response data in the requested type of object (String, JSON object, XML DOM object, array of bytes)
    getResponseData : function() {
      var resData = null;
      if (this.returnDataType == null) resData = this.getResponseText();
      else if (this.returnDataType == JSAF.AJAX_DATA_TYPE_TEXT) resData = this.getResponseText();
      else if (this.returnDataType == JSAF.AJAX_DATA_TYPE_JSON) resData = this.getResponseJson();
      else if (this.returnDataType == JSAF.AJAX_DATA_TYPE_XML) resData = this.getResponseXml();
      else if (this.returnDataType == JSAF.AJAX_DATA_TYPE_BYTE) resData = this.getResponseBody();
      else resData = this.getResponseText();
      return(resData);
    }
  };
  ajaxWorker.xmlHttpRequest.objAjaxWorker = ajaxWorker;	// set the AjaxWorker to the XmlHttpRequest
  if (ajaxWorker.async) {
    ajaxWorker.xmlHttpRequest.onreadystatechange = function () {
      // in this anonymous function "this" is the XmlHttpRequest object that calls this function
      if (JSAF.jsLogger.isDebugEnabled()) JSAF.jsLogger.debug(this.readyState);
      if (this.readyState == 4) {
        if (JSAF.jsLogger.isDebugEnabled()) {
          JSAF.jsLogger.debug("url=(" + this.objAjaxWorker.url + ")--resCode=(" + this.objAjaxWorker.getHttpResponseStatusCode() + 
             ")--resHeaders=(" + this.objAjaxWorker.getAllResponseHeaders() + ")");
        }
        if (this.objAjaxWorker.handler) this.objAjaxWorker.handler.call(this.objAjaxWorker, this.objAjaxWorker.getResponseData(), this.objAjaxWorker.handlerParam);
      } else {
        if (JSAF.jsLogger.isDebugEnabled()) {
          switch (this.readyState) {
            case 0: JSAF.jsLogger.debug("Uninitialized"); break;
            case 1: JSAF.jsLogger.debug("Opened"); break;
            case 2: JSAF.jsLogger.debug("Sent"); break;
            case 3: JSAF.jsLogger.debug("Receiving"); break;
          }
        }
      }
    };
  }
  return(ajaxWorker);
}

/** convert pixel value to int.
 * @param pixelValue the string
 * @return int
 */
JSAF.toInt = function(pixelValue) {
    if(!pixelValue) throw new Error("the param pixelValue must not be null, please check.");
    if(pixelValue.indexOf && pixelValue.indexOf("px") != -1) {
        return Number(pixelValue.replace(/px$/, ''));
    } else if(pixelValue.indexOf && pixelValue.indexOf("em") != -1) {
        return Number(pixelValue.replace(/em$/, ''));
    } else if(pixelValue.indexOf && pixelValue.indexOf("pt") != -1) {
        return Number(pixelValue.replace(/pt$/, ''));
    } else {
        return pixelValue;
    }
}
/** convert pixel value to pixel with 'px' or 'em' or 'pt' unit name.
 * @param pixelValue the string or int
 * @return str
 */
JSAF.toPixel = function(pixelValue, unitName) {
    if(!pixelValue && pixelValue != 0) throw new Error("the param pixelValue must not be null, please check.");
    if(!unitName) unitName = "px";
    if(pixelValue.indexOf && (pixelValue.indexOf("px") != -1 || pixelValue.indexOf("em") != -1 || pixelValue.indexOf("pt") != -1)) {
        return pixelValue;
    } else {
        return pixelValue + unitName;
    }
}

// end of file